<template>
    <div id="bmi-project">
        <h1 id="bmiCalculator">BMI 计算器</h1>
        <!-- gutter：元素之间的间距 -->
        <u-row gutter="12">
            <!-- span：占用的空间比例 -->
            <u-col span="10">
                <!-- 使用uView里面内置的上外边距class，值为20 -->
                <div class="u-margin-top-10">
                    <u-input
                        placeholder="请输入您的身高"
                        v-model.trim="height"
                        :clearable="true"
                        :border="true"
                        border-color="#2979ff"
                    />
                </div>
            </u-col>
            <u-col span="2">
                <!-- 使用uView里面内置的上外边距class，值为20 -->
                <!-- @confirm：确认 -->
                <u-select
                    v-model="isShowHeightSelect"
                    mode="single-column"
                    :list="heightList"
                    @confirm="heightSelectConfirm"
                ></u-select>
                <!-- 选择单位的按钮 -->
                <u-button @click="isShowHeightSelect = true" class="selectButton">{{ nowheightUnit }} ∨</u-button>
            </u-col>
        </u-row>

        <u-row gutter="12">
            <u-col span="10">
                <!-- 使用uView里面内置的上外边距class，值为20 -->
                <div class="u-margin-top-10 u-margin-bottom-10">
                    <u-input placeholder="请输入您的体重" v-model.trim="weight" :border="true" border-color="#2979ff" />
                </div>
            </u-col>
            <u-col span="2">
                <!-- 使用uView里面内置的上外边距class，值为20 -->
                <!-- @confirm：确认 -->
                <u-select
                    v-model="isShowWeightSelect"
                    mode="single-column"
                    :list="weightList"
                    @confirm="weightSelectConfirm"
                ></u-select>
                <u-button @click="isShowWeightSelect = true" class="selectButton">{{ nowWeightUnit }} ∨</u-button>
            </u-col>
        </u-row>

        <div id="calculateBMI"><u-button type="primary" @click="calculateBmi">计算BMI(身体质量指数)</u-button></div>

        <!-- 如果有显示选择单位，则隐藏图表的显示，否则画面上下结构会有异常（目前用z-index无效） -->
        <div v-show="!isShowHeightSelect && !isShowWeightSelect" id="dashBoardDiv">
            <!-- 仪表盘的绘制 -->
            <canvas
                canvas-id="MwcZmgZmddpwwAqTAkVYTzYvpUkKnXJb"
                id="MwcZmgZmddpwwAqTAkVYTzYvpUkKnXJb"
                type="2d"
                class="charts"
                @touchend="tap"
            />
        </div>

        <!-- 参数说明的div -->
        <div class="parameterDescription">
            <!-- 宫格布局（2列） -->
            <u-grid :col="2">
                <!-- 使用v-for遍历数组（注意！:key绑定的不能是一个对象，而是具体的字符串等数据，否则会有警告） -->
                <u-grid-item
                    v-for="description in bmiDescription"
                    :key="description.content"
                    :custom-style="{ padding: '10rpx 0 10rpx 0' }"
                >
                    <!-- span标签用于显示前面的点，背景颜色使用动态绑定的style -->
                    <span class="circle" :style="{ background: description.color }"></span>
                    <span>{{ description.content }}</span>
                </u-grid-item>
            </u-grid>
        </div>
    </div>
</template>

<script>
// 导入此BMI计算器项目封装的方法
import { heightList, weightList, bmiDescription } from "../jsFunction/bmiData";
// 局部引入图表库uCharts
import uCharts from "@/uni_modules/qiun-data-charts/js_sdk/u-charts/u-charts.js";
// uCharts对象的变量，到后面会往里面添加属性和值
let uChartsInstance = {};

export default {
    name: "bmi-project",
    data() {
        return {
            // 身高
            height: null,
            // 体重
            weight: null,
            // BMI结果
            bmi: "等待计算中···",

            // 是否要展示身高单位选择列表
            isShowHeightSelect: false,
            // 是否要展示体重单位选择列表
            isShowWeightSelect: false,
            // 目前的身高单位
            nowheightUnit: "厘米",
            // 目前的体重单位
            nowWeightUnit: "公斤",
            // 身高选择列表（使用import进来的自定义数据，这里是ES6的写法）
            heightList,
            // 体重选择列表
            weightList,

            // 图表所用到的参数
            cWidth: 750,
            cHeight: 500,
            pixelRatio: 2,

            // BMI各个参数的说明
            bmiDescription,
            // 仪表盘显示BMI结果的字体的颜色
            dashBoardFontColor: null
        };
    },
    // 页面加载完毕时
    onReady() {
        //这里的 750 对应 css .charts 的 width
        this.cWidth = uni.upx2px(850);
        //这里的 500 对应 css .charts 的 height
        this.cHeight = uni.upx2px(600);
        this.pixelRatio = uni.getSystemInfoSync().pixelRatio;
    },
    methods: {
        // 计算BMI
        calculateBmi() {
            // 在计算之前先进行非空判断
            if (this.height === null) {
                uni.showToast({
                    title: "身高不能为空！",
                    // 使用icon:"none"，可以解除字数的限制，但会把图标隐藏掉
                    // image可以使用自定义的图片，作为提示
                    image: "/static/error.png"
                });
                this.height = null;
            } else if (this.weight === null) {
                uni.showToast({
                    title: "体重不能为空！",
                    // 使用icon:"none"，可以解除字数的限制，但会把图标隐藏掉
                    // image可以使用自定义的图片，作为提示
                    image: "/static/error.png"
                });
            } 
            // 如果不为空
            else {
                // 目前的单位（赋值成局部变量，不用频繁访问data中的变量）
                let nowheightUnit = this.nowheightUnit;
                let nowWeightUnit = this.nowWeightUnit;

                // 如果单位与默认值不同，就对输入的值进行修改（最后体重要转换为“公斤”，身高要转换为“米”
                if (nowheightUnit === "厘米") {
                    // 在计算BMI时，统一会以“千克/公斤”和“米”作为单位，所以输入厘米时，会除以100转为米
                    this.height = this.height / 100;
                } else if (nowheightUnit === "英尺") {
                    this.height = this.height / 3.2808;
                } else if (nowheightUnit === "英寸") {
                    this.height = this.height / 39.37007874;
                }

                if (nowWeightUnit === "斤") {
                    // 如果单位改成斤，那么就将输入的体重除2，因为计算时以“公斤/千克”计算
                    this.weight = this.weight / 2;
                } else if (nowWeightUnit === "磅") {
                    // 磅转公斤的计算公式
                    this.weight = this.weight / 2.2046;
                } else if (nowWeightUnit === "吨") {
                    this.weight = this.weight / 0.001;
                }

                // 输入边界限制
                if (0.56 < this.height && this.height < 2.72) {
                    if (5.5 < this.weight && this.weight < 635) {
                        // 体质指数（BMI）= 体重（公斤）÷ 身高^2（米）
                        this.bmi = this.weight / Math.pow(this.height, 2);

                        // 计算出来的bmi值，除以40（封顶值）即能正确显示指针的位置
                        let dashBoardBmi = this.bmi / 40;
                        // 决定显示BMI的数字是什么颜色
                        if (dashBoardBmi <= 0.37) {
                            this.dashBoardFontColor = "#526cd2";
                        } else if (dashBoardBmi <= 0.4) {
                            this.dashBoardFontColor = "#2483ef";
                        } else if (dashBoardBmi <= 0.45) {
                            this.dashBoardFontColor = "#5cb3f5";
                        } else if (dashBoardBmi <= 0.63) {
                            this.dashBoardFontColor = "#2fc25b";
                        } else if (dashBoardBmi <= 0.75) {
                            this.dashBoardFontColor = "#ffc102";
                        } else if (dashBoardBmi <= 0.87) {
                            this.dashBoardFontColor = "#ff8135";
                        } else if (dashBoardBmi <= 1) {
                            this.dashBoardFontColor = "#fa3534";
                        } else if (dashBoardBmi > 1) {
                            this.dashBoardFontColor = "#ff0000";
                        }

                        let res = {
                            // 仪表盘的颜色分布比例（注意是比例！而不是数值，而且这里的数值都是从0开始的，不是一个接一个的）
                            categories: [
                                // BMI指数 < 15，体重过轻（严重消瘦）
                                { value: 0.37, color: "#526cd2" },
                                // BMI指数 < 16，体重过轻（中度消瘦）
                                { value: 0.4, color: "#2483ef" },
                                // BMI指数 < 18.5，体重过轻（轻度消瘦）
                                { value: 0.45, color: "#5cb3f5" },
                                // BMI指数 < 25，体重正常
                                { value: 0.63, color: "#2fc25b" },
                                // BMI指数 < 30，体重过重（肥胖前期）
                                { value: 0.75, color: "#ffc102" },
                                // BMI指数 < 35，肥胖I级（轻度肥胖）
                                { value: 0.87, color: "#ff8135" },
                                // BMI指数 < 40，肥胖II级（中度肥胖）
                                { value: 1, color: "#fa3534" }
                            ],
                            series: [
                                {
                                    name: "BMI",
                                    // 如果结果的比例大于1，证明以及超出“40”了，超出40就按仪表盘最大值计算，否则指针会旋转几圈在，造成Bug
                                    data: dashBoardBmi > 1 ? 1 : dashBoardBmi
                                }
                            ]
                        };
                        this.drawCharts("MwcZmgZmddpwwAqTAkVYTzYvpUkKnXJb", res);
                        // 最后将输入的数据清空
                        this.height = null;
                        this.weight = null;
                        // uni-app的非模态提示框，它弹出一个小信息，作为提醒或消息反馈来用
                        uni.showToast({
                            title: "计算成功!"
                        });
                    } else {
                        uni.showToast({
                            title: "体重有误！",
                            // 使用icon:"none"，可以解除字数的限制，但会把图标隐藏掉
                            // image可以使用自定义的图片，作为提示
                            image: "/static/error.png"
                        });
                        // 重新输入会比修改快
                        this.weight = null;
                    }
                } else {
                    uni.showToast({
                        title: "身高有误！",
                        // 使用icon:"none"，可以解除字数的限制，但会把图标隐藏掉
                        // image可以使用自定义的图片，作为提示
                        image: "/static/error.png"
                    });
                    this.height = null;
                }
            }
        },
        // 调整身高单位的下拉框，选择单位（如厘米、米等）
        heightSelectConfirm(e) {
            // 注意返回值为一个数组，单列时取数组的第一个元素即可(只有一个元素)
            this.nowheightUnit = e[0].label;
            // 选择完毕后，关闭显示
            this.isShowHeightSelect = false;
        },
        // 调整体重单位的下拉框
        weightSelectConfirm(e) {
            this.nowWeightUnit = e[0].label;
            // 选择完毕后，关闭显示
            this.isShowWeightSelect = false;
        },
        // 仪表盘的绘制方法
        drawCharts(id, data) {
            const query = uni.createSelectorQuery().in(this);
            query
                .select("#" + id)
                .fields({ node: true, size: true })
                .exec(res => {
                    if (res[0]) {
                        const canvas = res[0].node;
                        const ctx = canvas.getContext("2d");
                        canvas.width = res[0].width * this.pixelRatio;
                        canvas.height = res[0].height * this.pixelRatio;
                        uChartsInstance[id] = new uCharts({
                            type: "gauge",
                            context: ctx,
                            width: this.cWidth * this.pixelRatio,
                            height: this.cHeight * this.pixelRatio,
                            categories: data.categories,
                            series: data.series,
                            pixelRatio: this.pixelRatio,
                            animation: true,
                            background: "#FFFFFF",
                            color: [
                                "#1890FF",
                                "#91CB74",
                                "#FAC858",
                                "#EE6666",
                                "#73C0DE",
                                "#3CA272",
                                "#FC8452",
                                "#9A60B4",
                                "#ea7ccc"
                            ],
                            padding: undefined,
                            title: {
                                // BMI值的文字标题
                                name: this.bmi.toFixed(1),
                                fontSize: 25,
                                // 文字的标题
                                color: this.dashBoardFontColor,
                                offsetY: 50
                            },
                            subtitle: {
                                name: "我的BMI",
                                fontSize: 15,
                                color: "#666666",
                                offsetY: -50
                            },
                            extra: {
                                gauge: {
                                    type: "default",
                                    width: 30,
                                    labelColor: "#666666",
                                    startAngle: 0.75,
                                    endAngle: 0.25,
                                    // 仪表盘最小值（下限）
                                    startNumber: 0,
                                    // 仪表盘最大值（上限）
                                    endNumber: 40,
                                    labelFormat: "",
                                    splitLine: {
                                        fixRadius: 0,
                                        splitNumber: 10,
                                        width: 30,
                                        color: "#FFFFFF",
                                        childNumber: 5,
                                        childWidth: 12
                                    },
                                    pointer: {
                                        width: 24,
                                        color: "auto"
                                    }
                                }
                            }
                        });
                    } else {
                        console.error("[uCharts]: 未获取到 context");
                    }
                });
        },
        onPageScroll(e) {
            this.scrollTop = e.scrollTop;
        }
    }
};
</script>

<style lang="scss" scoped>
// template标签里的根div
#bmi-project {
    margin-left: 20rpx;
    margin-right: 20rpx;
}

// 标题
#bmiCalculator {
    font-size: 50rpx;
    margin-left: 4rpx;
    margin-top: 10rpx;
    margin-bottom: 10rpx;
    // 来源于开源字体库的字体样式（思源字体——Adobe与Google历时三年在2014年7月宣布推出的一款开源字体）
    font-family: "Source-Han-Light";
}

// 下列按钮——选择单位
.selectButton {
    position: relative;
    top: 6rpx;
}

// 计算BMI值
#calculateBMI {
    width: 400rpx;
    margin: 0 auto;
}

// 仪表盘的div
#dashBoardDiv {
    height: 450rpx;
}

// 仪表盘的大小
.charts {
    width: 100%;
    height: 510rpx;
    margin-top: 100rpx;
    right: 9%;
}

// 圆的样式
.circle {
    width: 20rpx;
    height: 20rpx;
    border-radius: 100%;
}

// 参数说明的div
.parameterDescription {
    // 通过屏幕视图来确定位置
    position: fixed;
    // 距离底部留一点点距离
    bottom: 10rpx;
}
</style>
